#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>

#include "btdm_host.h"
#include "btdm_utils.h"
#include "pan_api.h"
#include "fdb_app.h"

#include "app_config.h"
#include "app_bt.h"
#include "user_bt.h"
#include "ethernetif.h"
#include "FreeRTOS.h"

#ifndef LOG_ENABLE
#define LOG_ENABLE
#endif
#include "co_log.h"
#ifdef LOG_LOCAL_LEVEL
#undef LOG_LOCAL_LEVEL
#endif
#define LOG_LOCAL_LEVEL             LOG_LEVEL_INFO

#define max(a, b)   ((a) > (b) ? (a) : (b))
#define min(a, b)   ((a) > (b) ? (b) : (a))

const BtAccessModeInfo access_mode_nc = {
    .inqInterval = 0x800,
    .inqWindow = 0x12,
    .pageInterval = 0x800,
    .pageWindow = 0x12,
};

static BtHandler securityHandler;
static BtHandler globalHandler;
static BtSecurityParms secParms;

static char hf_codec[2];
uint8_t default_name[] = "FR30xx_SUN_BT";
//HfChannel hf_channel[NUM_BT_DEVICES];
//HfgChannel hfg_channel[NUM_BT_DEVICES];
//A2dpStream Stream[NUM_STREAMS];
//AvrcpChannel rcpCtChannel[NUM_BT_DEVICES];
//AvrcpChannel rcpTgChannel[NUM_BT_DEVICES];

HfChannel *hf_channel;
HfgChannel *hfg_channel;
A2dpStream *Stream;
AvrcpChannel *rcpCtChannel;
AvrcpChannel *rcpTgChannel;
//BtPacket *scoPacket;

uint8_t SbcSnkElements[]={
    A2DP_SBC_CODEC_INF_ELEMENT(
        /* 48000 and 44100 are required for SNK */ 
       A2DP_SBC_CODEC_FREQ_48000  | 
       A2DP_SBC_CODEC_FREQ_44100  | 
       A2DP_SBC_CODEC_FREQ_32000  | 
       A2DP_SBC_CODEC_FREQ_16000,

       /* All modes required for SNK */ 
       A2DP_SBC_CODEC_CHNL_MODE_MONO    | 
       A2DP_SBC_CODEC_CHNL_MODE_DUAL    | 
       A2DP_SBC_CODEC_CHNL_MODE_STEREO  | 
       A2DP_SBC_CODEC_CHNL_MODE_JOINT,

       /* One block size must be supported */ 
       A2DP_SBC_CODEC_BLOCKS_16  | 
       A2DP_SBC_CODEC_BLOCKS_12  | 
       A2DP_SBC_CODEC_BLOCKS_8   | 
       A2DP_SBC_CODEC_BLOCKS_4,

       /* Both 8 and 4 must be supported */ 
       A2DP_SBC_CODEC_SUBBANDS_8 | 
       A2DP_SBC_CODEC_SUBBANDS_4,

       /* Both allocation methods must  be supported in SNK */ 
       A2DP_SBC_CODEC_ALLOCATION_LOUDNESS | 
       A2DP_SBC_CODEC_ALLOCATION_SNR,

       /* Minium bitpool */
       2,

       /* Maximum bitpool */
       53)

    };

uint8_t AacSnkElements[] = {
    A2DP_AAC_CODEC_MPEG2_LC     |
    A2DP_AAC_CODEC_MPEG4_LC,
    
    A2DP_AAC_CODEC_FREQ_44100, 
    
    A2DP_AAC_CODEC_FREQ_48000   |
    A2DP_AAC_CODEC_CHNL_MONO    |
    A2DP_AAC_CODEC_CHNL_STEREO, 
    
    A2DP_AAC_CODEC_VBR,
    
    0x00, 0x00
    };

/* Source SBC Elements */ 
static uint8_t SbcSrcElements[] = {
    A2DP_SBC_CODEC_INF_ELEMENT(/* 48000 or 44100 is required for SRC  */ 
                               /*A2DP_SBC_CODEC_FREQ_48000 | */
                               A2DP_SBC_CODEC_FREQ_44100 /*| 
                               A2DP_SBC_CODEC_FREQ_32000 | 
                               A2DP_SBC_CODEC_FREQ_16000*/,

                               /* MONO and one other required for SRC */ 
                               A2DP_SBC_CODEC_CHNL_MODE_MONO    | 
                               A2DP_SBC_CODEC_CHNL_MODE_DUAL    | 
                               A2DP_SBC_CODEC_CHNL_MODE_STEREO  | 
                               A2DP_SBC_CODEC_CHNL_MODE_JOINT,

                               /* One block size must be supported */ 
                               A2DP_SBC_CODEC_BLOCKS_16 | 
                               A2DP_SBC_CODEC_BLOCKS_12 | 
                               A2DP_SBC_CODEC_BLOCKS_8  | 
                               A2DP_SBC_CODEC_BLOCKS_4,

                               /* SRC must support 8 subbands */ 
                               A2DP_SBC_CODEC_SUBBANDS_8 |
                               A2DP_SBC_CODEC_SUBBANDS_4,

                               /* SRC must support LOUDNESS */ 
                               A2DP_SBC_CODEC_ALLOCATION_LOUDNESS | 
                               A2DP_SBC_CODEC_ALLOCATION_SNR,

                               /* Minium bitpool */
                               2,

                               /* Maximum bitpool */
                               53)
};

AvdtpCodec sbcSnkCodec1;         //local codec information
AvdtpCodec aacSnkCodec1;
AvdtpCodec sbcSnkCodec2;         //local codec information
AvdtpCodec aacSnkCodec2;

AvdtpCodec          sbcSrcCodec1;
AvdtpCodec          sbcSrcCodec2;

static AvdtpCodec          cfgCodec;
static uint8_t                  cfgElements[4];
BtRemoteDevice *last_rem_dev = NULL;
extern void CMGR_SetAudioVoiceSettings(BtScoAudioSettings settings);

MeCommandToken *name_token;

static void name_result_handler(const BtEvent *event)
{
    printf("name result: %d,%d\r\n",event->eType,event->errCode);
	if((event->eType != BTEVENT_NAME_RESULT) || (event->p.meToken != name_token)){
        return;
    }
    if(event->errCode == 0){
        printf("$$$$$addr:%x,%x,name:%d,%s\r\n",event->p.meToken->p.name.bdAddr.A[0],event->p.meToken->p.name.bdAddr.A[1],event->p.meToken->p.name.io.out.len,event->p.meToken->p.name.io.out.name);
    }
    if(name_token){
        btdm_free(name_token);
        name_token = 0;
    }
}

void bt_name_query(BD_ADDR *addr, BtPageScanInfo *psi)
{
    BtStatus         status;

    ///doing name request
    if(name_token){
        return;
    }
    name_token = (MeCommandToken *)btdm_malloc(sizeof(MeCommandToken));
    memcpy(&name_token->p.name.bdAddr,addr,6);
    memcpy(&name_token->p.name.io.in.psi,psi,sizeof(BtPageScanInfo));
    //memset(&name_token.p.name.io.in.psi,0,sizeof(BtPageScanInfo));

    name_token->callback = name_result_handler;
    
    status = ME_GetRemoteDeviceName(name_token);
    if(status != BT_STATUS_PENDING){
        btdm_free(name_token);
        name_token = 0;
    }
    printf("bt_name_query: %x,%x\r\n",addr->A[0],addr->A[1]);
}

uint8_t bt_get_free_hf_channel(void)
{
    uint8_t index = 0;
    for(index = 0; index < NUM_BT_DEVICES; index++){
        if(hf_channel[index].state == HF_STATE_CLOSED){
            break;
        }
    }
    return index;
}
    
uint8_t bt_get_free_hfg_channel(void)
{
    uint8_t index = 0;
    for(index = 0; index < NUM_BT_DEVICES; index++){
        if(hfg_channel[index].state == HFG_STATE_CLOSED){
            break;
            }
        }
    return index;
}

uint8_t bt_get_free_a2dp_sink_stream(void)
{
    uint8_t index = 0;
    for(index = 0; index < NUM_BT_DEVICES; index++){
        //if(Is_a2dp_stream_registered(&Stream[index]) && (Stream[index].device->state == A2DP_DEV_STATE_DISCONNECTED)){
        if(Is_a2dp_stream_registered(&Stream[index]) && (Stream[index].device == NULL)){
            break;
        }
    }
    return index;
}

uint8_t bt_get_free_a2dp_source_stream(void)
{
    uint8_t index = 0;
    for(index = 2; index < NUM_STREAMS; index++){
        //if(Is_a2dp_stream_registered(&Stream[index]) && (Stream[index].device->state == A2DP_DEV_STATE_DISCONNECTED)){
        if(Is_a2dp_stream_registered(&Stream[index]) && (Stream[index].device == NULL)){
            break;
        }
    }
    return index;
}

uint8_t bt_get_free_avrcp_channel(void)
{
    uint8_t index = 0;
    for(index = 0; index < NUM_BT_DEVICES; index++){
        if(rcpCtChannel[index].chnl.conn.state == 0){
            break;
    }
    }   
    return index;
}

void bt_avrcp_register_notification(AvrcpChannel *chnl, uint16_t event_mask)
{
    AvrcpAdvancedPdu *avrcp_cmd;
    BtStatus status;
    printf("avrcp register event:%x\r\n",event_mask);
    if(event_mask & AVRCP_ENABLE_PLAY_STATUS_CHANGED){
        ///注册播放状态改变事件，当TG播放状态改变时，产生AVRCP_EVENT_ADV_NOTIFY事件
        avrcp_cmd = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
        avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
        status = AVRCP_CtRegisterNotification(chnl, avrcp_cmd, AVRCP_EID_MEDIA_STATUS_CHANGED, 0);
        if (status != BT_STATUS_PENDING) {
            btdm_free(avrcp_cmd->parms);
            btdm_free(avrcp_cmd);
        }
    }
    if(event_mask & AVRCP_ENABLE_TRACK_CHANGED){
        ///注册歌曲改变事件，当TG播放状态改变时，产生AVRCP_EVENT_ADV_NOTIFY事件
        avrcp_cmd = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
        avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
        status = AVRCP_CtRegisterNotification(chnl, avrcp_cmd, AVRCP_EID_TRACK_CHANGED, 0);
        if (status != BT_STATUS_PENDING) {
            btdm_free(avrcp_cmd->parms);
            btdm_free(avrcp_cmd);
        }
    }
    if(event_mask & AVRCP_ENABLE_VOLUME_CHANGED){
        ///注册歌曲改变事件，当TG播放状态改变时，产生AVRCP_EVENT_ADV_NOTIFY事件
        avrcp_cmd = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
        avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
        status = AVRCP_CtRegisterNotification(chnl, avrcp_cmd, AVRCP_EID_VOLUME_CHANGED, 0);
        if (status != BT_STATUS_PENDING) {
            btdm_free(avrcp_cmd->parms);
            btdm_free(avrcp_cmd);
        }
    }
}

/// 0--- not supported type, 1---cvsd, 2---msbc, 3---cvsd&msbc
static uint8_t bt_parse_hf_codec_type(uint8_t *data)
{
    uint8_t ret = 0;
    uint8_t index,len;
    len = strlen((void *)data);
    
    for(uint8_t i = 0; i < len; i++)
    {
        if((data[i] > '9') || (data[i] < '1')){
            continue;
        }
        if(ret == 0){
            if(data[i] == '1'){
                ret = 1;
            }
            else if(data[i] == '2'){
                ret = 2;
            }
            else{
                ret = 0;
                break;
            }
        }
        else if(ret == 1){
            if(data[i] == '2'){
                ret = 3;
            }
            else{
                ret = 0;
                break;
            }
        }
        else if(ret == 2){
            if(data[i] == '1'){
                ret = 3;
            }
            else{
                ret = 0;
                break;
            }
        }       
    }
    
    return ret;
}

static void security_event_handler(const BtEvent *event)
{
    //BtDeviceContext  *bdc;
    
    switch(event->eType) {
        case BTEVENT_PIN_REQ:
            SEC_SetPin(event->p.remDev, (const uint8_t *)"0000", 4, BPT_SAVE_TRUSTED);
            break;
        case BTEVENT_AUTHORIZATION_REQ:
            SEC_Authorize(event->p.remDev, TRUE, TRUE);
            break;
        //case BTEVENT_PAIRING_COMPLETE:
        //    bdc = DS_FindDevice(&event->p.pairingInfo.remDev->bdAddr);
        //    break;
        default:
            //co_printf("SecurityEventHandler: event--0x%02x is not implemented.\r\n", event->eType);
            break;
    }
}

static void me_callback(const BtEvent *event)
{
    LOG_INFO(NULL, "me_callback :%d,%d\r\n",event->eType,event->errCode);

    switch (event->eType) {
        case BTEVENT_HCI_INITIALIZED:
            break;
        
        case BTEVENT_INQUIRY_CANCELED:
            //printf("inquiry canceled\r\n");
            bt_update_conn_status(BT_INFO_INQ_CANCEL, NULL, NULL);
            break;
        
        case BTEVENT_INQUIRY_RESULT:
//        {
//            uint8_t name[MAX_NAME_SIZE];
//            uint8_t len;
//            printf("inq result: %x\r\n",event->p.inqResult.classOfDevice);
//            printf("addr:0x%02x%02x%02x%02x%02x%02x\r\n",event->p.inqResult.bdAddr.A[0],event->p.inqResult.bdAddr.A[1],event->p.inqResult.bdAddr.A[2],
//                    event->p.inqResult.bdAddr.A[3],event->p.inqResult.bdAddr.A[4],event->p.inqResult.bdAddr.A[5]);
//            printf("rssi:%d,name: ",event->p.inqResult.rssi);
//            bt_parse_inq_result((uint8_t *)event->p.inqResult.extInqResp,name,&len);   
//            for(uint8_t i = 0; i < len; i++)
//            {
//                printf("%c",name[i]);
//            }
//            printf("\r\n");
//        }
            bt_update_conn_status(BT_INFO_INQ_RESULT, NULL, &event->p.inqResult);
        break;
        
        case BTEVENT_INQUIRY_COMPLETE:
            bt_update_conn_status(BT_INFO_INQ_COMP, NULL, NULL);
            //ME_Inquiry(BT_IAC_GIAC, 5, 5);
            break;
            
        case BTEVENT_LINK_CONNECT_REQ:
            ME_AcceptIncomingLink(event->p.remDev, BCR_SLAVE);

            break;
        case BTEVENT_LINK_CONNECT_IND:
//            if(user_bt_env.bt_connect_cb){
//                user_bt_env.bt_connect_cb(BTEVENT_LINK_CONNECT_IND, &event->p.remDev->bdAddr, event->errCode);
//            }
            last_rem_dev = event->p.remDev;
            bt_connect_act_cmp(BT_EVENT_CON_IND, event->errCode, event->p.remDev);
            break;

        case BTEVENT_LINK_CONNECT_CNF:
//            if(user_bt_env.bt_connect_cb){
//                user_bt_env.bt_connect_cb(BTEVENT_LINK_CONNECT_CNF, &event->p.remDev->bdAddr, event->errCode);
//            }
            last_rem_dev = event->p.remDev;
            bt_connect_act_cmp(BT_EVENT_CON_CNF, event->errCode, event->p.remDev);
            break;
            
        case BTEVENT_LINK_DISCONNECT:
//            if(user_bt_env.bt_disconnect_cb){
//                user_bt_env.bt_disconnect_cb(&event->p.bdAddr, event->errCode);
//            }
            bt_connect_act_cmp(BT_EVENT_DISCONNECT, event->errCode, event->p.remDev);

            break;

        case BTEVENT_ACCESSIBLE_CHANGE:
            printf("access state = %d\r\n",event->p.aMode);
//            if(user_bt_env.bt_access_change_cb){
//                user_bt_env.bt_access_change_cb(event->p.aMode);
//            }
            bt_connect_act_cmp(BT_EVENT_ACC_CHG, event->p.aMode, NULL);
            break;

        case BTEVENT_MODE_CHANGE:
            if(event->p.modeChange.curMode == 0){
                printf("Active Mode.\r\n");
            }else{
                printf("Sniff Mode.\r\n");
            }
            break;
            
        case BTEVENT_ROLE_CHANGE:
            printf("role = %d\r\n",event->p.roleChange.newRole);
            break;
        case BTEVENT_HCI_COMMAND_SENT:
            break;
        default:
            break;
    }
}

static void hf_callback(HfChannel *Chan, HfCallbackParms *Info)
{
//    static bool scoPacketSent = true;

    if ((Info->event != HF_EVENT_AUDIO_DATA)
        && (Info->event != HF_EVENT_AUDIO_DATA_SENT)) {
        printf("hf_callback: event = %d.\r\n", Info->event);
    }

    switch(Info->event){
        case HF_EVENT_GATEWAY_FEATURES:
            //printf("gw feature: %x\r\n",Info->p.features);
            if((Info->p.features & HFG_FEATURE_CODEC_NEGOTIATON) && (HF_GetSupportedFeature() & HF_FEATURE_CODEC_NEGOTIATION_CFG)){
                CMGR_SetAudioVoiceSettings(0x63);
            }else{
                CMGR_SetAudioVoiceSettings(0x60);
            }
            break;
        case HF_EVENT_SERVICE_CONNECT_REQ:
            bt_update_conn_status(BT_PROFILE_HF_CONN_REQ, Chan, Info);
            break;
    
        case HF_EVENT_SERVICE_CONNECTED:
            LOG_INFO(NULL,"hf connected\r\n");
            //A2DP_OpenStream(&Stream[0], &Info->p.remDev->bdAddr);
            bt_update_conn_status(BT_PROFILE_HF_CONN, Chan, Info);
            break;
            
        case HF_EVENT_SERVICE_DISCONNECTED:
            LOG_INFO(NULL,"hf disconnected %d\r\n",Info->errCode);
            bt_update_conn_status(BT_PROFILE_HF_DISCONN, Chan, Info);
            break;

        case HF_EVENT_CALL_IND:
            bt_update_conn_status(BT_PROFILE_HF_CALL, Chan, Info);

            break;
            
        case HF_EVENT_CALLSETUP_IND:
            bt_update_conn_status(BT_PROFILE_HF_CALLSETUP, Chan, Info);
            break;
            
        case HF_EVENT_CALLHELD_IND:
            break;
            
        case HF_EVENT_RING_IND:
            bt_update_conn_status(BT_PROFILE_HF_RING, Chan, Info);
            break;
            
        case HF_EVENT_CALLER_ID_NOTIFY:
            break;
            
        case HF_EVENT_SPEAKER_VOLUME:
            bt_update_conn_status(BT_PROFILE_HF_SPK_VOL, Chan, Info);
            break;
        case HF_EVENT_CURRENT_CALL_STATE:
            bt_update_conn_status(BT_PROFILE_HF_CURRENT_CALL, Chan, Info);            
            break;
        case HF_EVENT_AT_RESULT_DATA:
            bt_update_conn_status(BT_PROFILE_HF_AT_RESULT, Chan, Info);
            break;
        case HF_EVENT_AUDIO_CONNECTED:
            bt_update_conn_status(BT_PROFILE_HF_AUDIO_CONN, Chan, Info);
            break;
            
        case HF_EVENT_AUDIO_DISCONNECTED:
            bt_update_conn_status(BT_PROFILE_HF_AUDIO_DISCONN, Chan, Info);
            break;
        
        case HF_EVENT_AUDIO_DATA:
            bt_update_conn_status(BT_PROFILE_HF_AUDIO_DATA, Chan, Info);
            break;
        
        case HF_EVENT_AUDIO_DATA_SENT:
//            scoPacketSent = true;
            btdm_free(Info->p.audioPacket->data);
            btdm_free(Info->p.audioPacket);
            break;

#if HF_CODEC_NEG == XA_ENABLED
        case HF_EVENT_CODEC_NEGOTIATION:
            {
                HfCommand *cmd;
                
                if(Info->p.codecID == 1){
                    //cvsd
                    CMGR_SetAudioVoiceSettings(0x60);
                }else if(Info->p.codecID == 2){
                    //msbc
                    CMGR_SetAudioVoiceSettings(0x63);
                }
                BtStatus ret = BT_STATUS_NO_RESOURCES;
                cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
                if(cmd != NULL){
                    hf_codec[0] = Info->p.codecID + '0';
                    hf_codec[1] = 0;
                    ret = HF_CodecConnectionSetup((HfChannel *)Chan, hf_codec, cmd);
                }
                if(ret != BT_STATUS_PENDING){
                    btdm_free((void *)cmd);
                }
            }
            break;
#endif

        case HF_EVENT_COMMAND_COMPLETE:
            btdm_free((uint8_t *)Info->p.command);
            break;
        
        default:
            break;
    }
}

static void hfg_callback(HfgChannel *Chan, HfgCallbackParms *Info)
{
    BtStatus status;
    HfgResponse *rsp;
    uint8_t codec_type;
    
    if ((Info->hfgEvent != HFG_EVENT_AUDIO_DATA)
        && (Info->hfgEvent != HFG_EVENT_AUDIO_DATA_SENT)) {
    LOG_INFO(NULL, "hfg_callback :%d\r\n",Info->hfgEvent);
    }
    switch (Info->hfgEvent) {
        case HFG_EVENT_HANDSFREE_FEATURES:
            printf("remote hf feature: %x\r\n",Info->p.features);
//            if(Info->p.features & HF_FEATURE_CODEC_NEGOTIATION){
//                CMGR_SetAudioVoiceSettings(0x63);
//            }else{
//                CMGR_SetAudioVoiceSettings(0x60);
//            }
            break;
        case HFG_EVENT_SERVICE_CONNECT_REQ:
            bt_update_conn_status(BT_PROFILE_HFG_CONN_REQ, Chan, Info);
            break;

        case HFG_EVENT_SERVICE_CONNECTED:
            bt_update_conn_status(BT_PROFILE_HFG_CONN, Chan, Info);
            break;
        case HFG_EVENT_SERVICE_DISCONNECTED:
            bt_update_conn_status(BT_PROFILE_HFG_DISCONN, Chan, Info);
            break;
        case HFG_EVENT_AUDIO_CONNECTED:
            {
                HfgResponse *rsp;
                rsp = (HfgResponse *)btdm_malloc(sizeof(HfgResponse));
                HFG_SetIndicatorValue(Chan, HFG_IND_CALL, TRUE, rsp);
                bt_update_conn_status(BT_PROFILE_HFG_AUDIO_CONN, Chan, Info);        
            }
            break;
        case HFG_EVENT_AUDIO_DISCONNECTED:
            {
                HfgResponse *rsp;
                rsp = (HfgResponse *)btdm_malloc(sizeof(HfgResponse));
                HFG_SetIndicatorValue(Chan, HFG_IND_CALL, FALSE, rsp);
                bt_update_conn_status(BT_PROFILE_HFG_AUDIO_DISCONN, Chan, Info);        
            }
            break;
        case HFG_EVENT_AT_COMMAND_DATA:
            for(uint8_t i = 0; i < Info->p.data->dataLen; i++)
                printf("%c",Info->p.data->rawData[i]);
            printf("\r\n");
        
            rsp = (HfgResponse *)btdm_malloc(sizeof(HfgResponse));
            status = HFG_SendOK(Chan,rsp);
            if(status != BT_STATUS_PENDING){
                btdm_free((void *)rsp);
            }
//            if(memcmp(Info->p.data->rawData,"AT+IPHONEACCEV=1,1,9",sizeof("AT+IPHONEACCEV=1,1,9")) == 0)
//            {
//                status = BT_STATUS_NO_RESOURCES;
//                rsp = (HfgResponse *)btdm_malloc(sizeof(HfgResponse));
//                if(rsp != NULL){
//                    status = HFG_CreateCodecConnection(&hfg_channel[0], 1, rsp);
//                }
//                if(status != BT_STATUS_PENDING){
//                    btdm_free((void *)rsp);
//                }
//                printf("status = %d\r\n",status);
//            }
            break;
        case HFG_EVENT_SUPPORTED_CODEC:
            codec_type = bt_parse_hf_codec_type(Info->p.ptr);
            if(codec_type >= 2){
                CMGR_SetAudioVoiceSettings(0x63);
            }else{
                CMGR_SetAudioVoiceSettings(0x60);
            }
            break;
        case HFG_EVENT_AUDIO_DATA:
            {

//                if(Info->p.audioData->len == 120){
//                    fputc('M',NULL);
//                }else if(Info->p.audioData->len == 60){
//                    fputc('m',NULL);
//                }
//                printf("audio data: ");
//                for(uint8_t i = 0; i < Info->p.audioData->len; i++)
//                {
//                    printf("%02x ",Info->p.audioData->data[i]);
                //}
//                printf("\r\n");
                bt_update_conn_status(BT_PROFILE_HFG_AUDIO_DATA, Chan, Info);     
            }
            break;
        
        case HFG_EVENT_AUDIO_DATA_SENT:
            //fputc('s',NULL);
            btdm_free((void *)Info->p.audioPacket->data);
            btdm_free((void *)Info->p.audioPacket);
            break;

        case HFG_EVENT_CODEC_CONNECTION_RSP:
            codec_type = bt_parse_hf_codec_type(Info->p.ptr);
            printf("hfg codec connection rsp: %s,type = %d\r\n",Info->p.ptr,codec_type);
            if(codec_type >= 2){
                CMGR_SetAudioVoiceSettings(0x63);
            }else{
                CMGR_SetAudioVoiceSettings(0x60);
            }
            HFG_CreateAudioLink(&hfg_channel[0],HFG_AR_LOCAL_USER_ACTION);
            break;
        case HFG_EVENT_RESPONSE_COMPLETE:
            
            btdm_free((void *)Info->p.response);
            break;

    }
}

static void a2dp_callback(A2dpStream *cbStream, const A2dpCallbackParms *Parms)
{
    AvdtpCodec       *codec;
    uint8_t               *elements;
    uint8_t               *reqElements;
    uint8_t error = A2DP_ERR_NO_ERROR;

    if((Parms->event != A2DP_EVENT_STREAM_DATA_IND ) && (Parms->event != A2DP_EVENT_STREAM_SBC_PACKET_SENT)) {
        LOG_INFO(NULL, "a2dp callback event=%d,%x,%x\r\n", Parms->event,Parms->error,Parms->discReason);
    }
    switch(Parms->event) {
        case A2DP_EVENT_STREAM_OPEN_IND:
            bt_update_conn_status(BT_PROFILE_A2DP_OPEN_IND, cbStream, Parms);
            if (AVDTP_CODEC_TYPE_SBC == Parms->p.configReq->codec.codecType) {
                codec = A2DP_GetRegisteredCodec(cbStream);
                elements = codec->elements;
                reqElements = Parms->p.configReq->codec.elements;
                printf("a2dp local codec: %x,%x,%x,%x\r\n",elements[0],elements[1],elements[2],elements[3]);
                printf("a2dp remote codec: %x,%x,%x,%x\r\n",reqElements[0],reqElements[1],reqElements[2],reqElements[3]);
        
                /* Check requested elements with registered elements */ 
                if (!(reqElements[0] & (elements[0] & 0xF0))) {
                    error = A2DP_ERR_NOT_SUPPORTED_SAMP_FREQ;
                } else if (!(reqElements[0] & (elements[0] & 0x0F))) {
                    error = A2DP_ERR_NOT_SUPPORTED_CHANNEL_MODE;
                } else if (!(reqElements[1] & (elements[1] & 0x0C))) {
                    error = A2DP_ERR_NOT_SUPPORTED_SUBBANDS;
                } else if (!(reqElements[1] & (elements[1] & 0x03))) {
                    error = A2DP_ERR_NOT_SUPPORTED_ALLOC_METHOD;
                } else if (reqElements[2] < elements[2]) {
                    error = A2DP_ERR_NOT_SUPPORTED_MIN_BITPOOL_VALUE;
                } else if (reqElements[3] > elements[3]) {
                    error = A2DP_ERR_NOT_SUPPORTED_MAX_BITPOOL_VALUE;
                }
                
//                if(error == A2DP_ERR_NO_ERROR){
//                    
//                    sbcinfo.bitPool = reqElements[3];
//                    
//                    if(reqElements[0]&0x20){
//                        sbcinfo.sampleFreq = 2;//44100
//                    }
//                    else{
//                        sbcinfo.sampleFreq = 3;//48000
//                    }
//                    
//                    if(reqElements[1]&0x01){
//                        sbcinfo.allocMethod = 0;//loudness
//                    }
//                    else{
//                        sbcinfo.allocMethod = 1;//snr
//                    }
//                }
//                codec->freq = (reqElements[0] & 0xF0) >> 5;
                A2DP_OpenStreamRspWithSinkDelay(cbStream, error, 
                                                         AVDTP_SRV_CAT_MEDIA_CODEC,
                                                         0);
            } 
            else if(AVDTP_CODEC_TYPE_MPEG2_4_AAC== Parms->p.configReq->codec.codecType) {
                codec = A2DP_GetRegisteredCodec(cbStream);
                elements = codec->elements;
                reqElements = Parms->p.configReq->codec.elements;
//                if(reqElements[1] & 0x01) {
//                    codec->freq = 1;
//                }
//                else if(reqElements[2] & 0x80) {
//                    codec->freq = 0;
//                }
                
                A2DP_OpenStreamRspWithSinkDelay(cbStream, error, 
                                                         AVDTP_SRV_CAT_MEDIA_CODEC,
                                                         0);
            }
            else {
                /* Refuse to accept incoming con to MP3 stream for now */
                A2DP_OpenStreamRspWithSinkDelay(cbStream, 
                                                         AVRCP_ERR_UNKNOWN_ERROR, 
                                                         AVDTP_SRV_CAT_MEDIA_CODEC,
                                                         0);
            }
            break;
        case A2DP_EVENT_CODEC_INFO:
            printf("codec info: %x,%x,%x,%x\r\n",Parms->p.codec->elements[0],Parms->p.codec->elements[1],Parms->p.codec->elements[2],Parms->p.codec->elements[3]);
            /* Found a matching codec */ 
            codec = A2DP_GetRegisteredCodec(cbStream);
            elements = codec->elements;
            
            /* Save the remote codec information.  Selection 
             * of capabilities will be made from the UI.  
             */ 
            cfgCodec.codecType = Parms->p.codec->codecType;
            cfgCodec.elemLen = Parms->p.codec->elemLen;
            
            cfgCodec.elements = cfgElements;
            if (cfgCodec.codecType == AVDTP_CODEC_TYPE_SBC) {
            
                /* Only the matching codec information 
                 * elements can be selected.  
                 */ 
                cfgCodec.elements[0] 
                    = (uint8_t)(Parms->p.codec->elements[0] & elements[0]);
                cfgCodec.elements[1] 
                    = (uint8_t)(Parms->p.codec->elements[1] & elements[1]);
                cfgCodec.elements[2] 
                    = (uint8_t)(max(Parms->p.codec->elements[2], elements[2]));
                cfgCodec.elements[3] 
                    = (uint8_t)(min(Parms->p.codec->elements[3], elements[3]));
            }
            break;
        case A2DP_EVENT_GET_CONFIG_IND:
            LOG_INFO(NULL, "codec type=%d,%x,%x,%x,%x\r\n",cfgCodec.codecType,cfgCodec.elements[0],cfgCodec.elements[1],cfgCodec.elements[2],cfgCodec.elements[3]);
                    /* Make sure something valid is configured for each field */ 
            if (cfgCodec.codecType == AVDTP_CODEC_TYPE_SBC) {

                if (  (cfgCodec.elements[0] & 0xF0) 
                   && (cfgCodec.elements[0] & 0x0F) 
                   && (cfgCodec.elements[1] & 0xF0) 
                   && (cfgCodec.elements[1] & 0x0C) 
                   && (cfgCodec.elements[1] & 0x03)) {

                    /* Pick the sampling rate supported */ 
                    if (cfgCodec.elements[0] & 0x80) {
                        cfgCodec.elements[0] &= 0x8F;
                    } else if (cfgCodec.elements[0] & 0x40) {
                        cfgCodec.elements[0] &= 0x4F;
                    } else if (cfgCodec.elements[0] & 0x20) {
                        cfgCodec.elements[0] &= 0x2F;
                    } else if (cfgCodec.elements[0] & 0x10) {
                        cfgCodec.elements[0] &= 0x1F;
                    }

                    /* Pick the channel mode */ 
                    if (cfgCodec.elements[0] & 0x01) {
                        cfgCodec.elements[0] &= 0xF1;
                    } else if (cfgCodec.elements[0] & 0x02) {
                        cfgCodec.elements[0] &= 0xF2;
                    } else if (cfgCodec.elements[0] & 0x04) {
                        cfgCodec.elements[0] &= 0xF4;
                    } else if (cfgCodec.elements[0] & 0x08) {
                        cfgCodec.elements[0] &= 0xF8;
                    }

                    /* Pick the block length */ 
//                    if (cfgCodec.elements[1] & 0x80) {
//                        cfgCodec.elements[1] &= 0x8F;
//                    } else if (cfgCodec.elements[1] & 0x40) {
//                        cfgCodec.elements[1] &= 0x4F;
//                    } else if (cfgCodec.elements[1] & 0x20) {
//                        cfgCodec.elements[1] &= 0x2F;
//                    } else if (cfgCodec.elements[1] & 0x10) {
//                        cfgCodec.elements[1] &= 0x1F;
//                    }
                        cfgCodec.elements[1] &= 0x1F;

                    /* Pick the number of subbands */ 
                    if (cfgCodec.elements[1] & 0x04) {
                        cfgCodec.elements[1] &= 0xF7;
                    } else if (cfgCodec.elements[1] & 0x08) {
                        cfgCodec.elements[1] &= 0xFB;
                    }

                    /* Pick the allocation method */ 
                    if (cfgCodec.elements[1] & 0x01) {
                        cfgCodec.elements[1] &= 0xFD;
                    }
                    else if (cfgCodec.elements[1] & 0x02) {
                        cfgCodec.elements[1] &= 0xFE;
                    }

                    /* Select Min/Max Bitpools */ 
                    cfgCodec.elements[2] = cfgCodec.elements[2];
                    cfgCodec.elements[3] = cfgCodec.elements[3];
                    
//                    /* copy codec info to local*/
//                    sbcinfo.bitPool = cfgCodec.elements[3];
//                    
//                    if(cfgCodec.elements[0]&0x20){
//                        sbcinfo.sampleFreq = 2;//44100
//                    }
//                    else{
//                        sbcinfo.sampleFreq = 3;//48000
//                    }
//                    
//                    if(cfgCodec.elements[1]&0x01){
//                        sbcinfo.allocMethod = 0;//loudness
//                    }
//                    else{
//                        sbcinfo.allocMethod = 1;//snr
//                    }                    
                    A2DP_SetStreamConfigWithSinkDelay(cbStream, &cfgCodec, 0, 0);
                }
                else{
                    A2DP_CloseStream(cbStream);
                }
            }
            break;
        case A2DP_EVENT_STREAM_OPEN:
            LOG_INFO(NULL,"a2dp connected\r\n");
            bt_update_conn_status(BT_PROFILE_A2DP_CONN, cbStream, Parms);
            break;
        case A2DP_EVENT_STREAM_START_IND:
            A2DP_StartStreamRsp(cbStream, A2DP_ERR_NO_ERROR);
            break;
        case A2DP_EVENT_STREAM_STARTED:       
            bt_update_conn_status(BT_PROFILE_A2DP_PLAYING, cbStream, Parms);
            break;
        case A2DP_EVENT_STREAM_SUSPENDED:
            bt_update_conn_status(BT_PROFILE_A2DP_SUSPEND, cbStream, Parms);
            break;
        case A2DP_EVENT_STREAM_CLOSED:
            bt_update_conn_status(BT_PROFILE_A2DP_DISCONN, cbStream, Parms);
            break;
        case A2DP_EVENT_STREAM_DATA_IND:
            bt_update_conn_status(BT_PROFILE_A2DP_STREAM_DATA, cbStream, Parms);
            break;
        case A2DP_EVENT_STREAM_SBC_PACKET_SENT:
            bt_update_conn_status(BT_PROFILE_A2DP_SBC_PKT_SENT, cbStream, Parms);
            break;
        default:
            break;
    }
}

static void avrcp_callback(AvrcpChannel *chnl, const AvrcpCallbackParms *Parms)
{
    AvrcpAdvancedPdu *avrcp_cmd;
    BtStatus status;

    LOG_INFO(NULL, "avrcp callback event=%d, status=%d, chnl=%x\r\n",Parms->event, Parms->status, chnl);
    

    switch (Parms->event) {
        case AVRCP_EVENT_CONNECT_IND:
            AVRCP_ConnectRsp(chnl,TRUE);
            break;
        case AVRCP_EVENT_CONNECT:
            {
                avrcp_cmd = btdm_malloc(sizeof(AvrcpAdvancedPdu));
                avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
                status = AVRCP_CtGetCapabilities((AvrcpChannel *)chnl, avrcp_cmd, AVRCP_CAPABILITY_EVENTS_SUPPORTED);
                if (status != BT_STATUS_PENDING) {
                    btdm_free(avrcp_cmd->parms);
                    btdm_free(avrcp_cmd);
                }
                AVRCP_TgSetEventMask(chnl,AVRCP_ENABLE_VOLUME_CHANGED);
            }
            LOG_INFO(NULL, "avrcp connected\r\n");
            bt_update_conn_status(BT_PROFILE_AVRCP_CONN, chnl, Parms);
            break;
        case AVRCP_EVENT_DISCONNECT:
            LOG_INFO(NULL, "avrcp disconnected\r\n");
            bt_update_conn_status(BT_PROFILE_AVRCP_DISCONN, chnl, Parms);
            break;
        
        case AVRCP_EVENT_ADV_INFO:
            if(Parms->advOp == AVRCP_OP_SET_ABSOLUTE_VOLUME) {
                //LOG_INFO("SET_ABSOLUTE_VOLUME is %d.\r\n", event->param.adv.info.volume);
                printf("+VOL:%02x\r\n",Parms->p.adv.info.volume); 
            }
            break;
        case AVRCP_EVENT_ADV_RESPONSE:
            ///本地查询TG支持事件，返回消�?
            /**/
            if((Parms->advOp == AVRCP_OP_GET_CAPABILITIES)
                &&(Parms->p.adv.rsp.capability.type == AVRCP_CAPABILITY_EVENTS_SUPPORTED)){
                //printf("cap eventmaskk = %x\r\n",event->param.adv.rsp.capability.info.eventMask);
                uint16_t event_mask = Parms->p.adv.rsp.capability.info.eventMask;
                bt_update_conn_status(BT_PROFILE_AVRCP_EVENT_SUPPORT, chnl, &event_mask);
            }
                
            ///本地注册事件，返回的当前状�?
            if(Parms->advOp == AVRCP_OP_REGISTER_NOTIFY) {
                ///播放状态改变通知的当前状�?
                if(Parms->p.adv.notify.event == AVRCP_EID_MEDIA_STATUS_CHANGED) {

                }
                else if(Parms->p.adv.notify.event == AVRCP_EID_TRACK_CHANGED){
                    //user_bt_get_media_info();
                }
            }
            
            if(Parms->advOp == AVRCP_OP_GET_MEDIA_INFO){
                uint16_t len0 = Parms->p.adv.rsp.element.txt[0].length;
                uint16_t len1 = Parms->p.adv.rsp.element.txt[1].length;
                printf("media info: %d,%s,%d\r\n",len0,Parms->p.adv.rsp.element.txt[0].string,len1);
            }
            
            if(Parms->advOp == AVRCP_OP_GET_PLAY_STATUS){
                printf("media length:%d,pos:%d,status:%d\r\n",Parms->p.adv.rsp.playStatus.length,Parms->p.adv.rsp.playStatus.position,Parms->p.adv.rsp.playStatus.mediaStatus);
            }           

        break;        
        
        case AVRCP_EVENT_PANEL_CNF:
            if(Parms->p.panelCnf.press == TRUE){
                switch(Parms->p.panelCnf.operation) {
                    case AVRCP_POP_PAUSE:
                    AVRCP_SetPanelKey(chnl, AVRCP_POP_PAUSE, FALSE);
                    break;
                    case AVRCP_POP_PLAY:
                    AVRCP_SetPanelKey(chnl, AVRCP_POP_PLAY, FALSE);
                    break;
                    case AVRCP_POP_FORWARD:
                    AVRCP_SetPanelKey(chnl, AVRCP_POP_FORWARD, FALSE);
                    break;
                    case AVRCP_POP_BACKWARD:
                    AVRCP_SetPanelKey(chnl, AVRCP_POP_BACKWARD, FALSE);
                    break;
                    case AVRCP_POP_STOP:
                    AVRCP_SetPanelKey(chnl, AVRCP_POP_STOP, FALSE);
                    break;
                }
            }
        break;
            
        case AVRCP_EVENT_ADV_NOTIFY:
            {
                const AvrcpAdvNotifyParms *avrcp_ntf = &Parms->p.adv.notify;
                if(Parms->errorCode == AVRCP_ERR_NO_ERROR) {
                    switch(avrcp_ntf->event) {
                        case AVRCP_EID_TRACK_CHANGED:
                        {
                            avrcp_cmd = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
                            avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
                            status = AVRCP_CtRegisterNotification(chnl, avrcp_cmd, AVRCP_EID_TRACK_CHANGED, 0);
                            if (status != BT_STATUS_PENDING) {
                                btdm_free(avrcp_cmd->parms);
                                btdm_free(avrcp_cmd);
                            }                        
                        }
                        break;

                        case AVRCP_EID_MEDIA_STATUS_CHANGED:
                        {
                            ///重新注册播放状态改变通知消息
                            uint8_t media_status = Parms->p.adv.notify.p.mediaStatus;
                            bt_update_conn_status(BT_PROFILE_AVRCP_MEDIA_STATUS, chnl, &media_status);
                            
                            avrcp_cmd = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
                            avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
                            status = AVRCP_CtRegisterNotification(chnl, avrcp_cmd, AVRCP_EID_MEDIA_STATUS_CHANGED, 0);
                            if (status != BT_STATUS_PENDING) {
                                btdm_free(avrcp_cmd->parms);
                                btdm_free(avrcp_cmd);
                            }                        
                        }
                        break;     
                    }
                }
            }
            break;
            
        case AVRCP_EVENT_ADV_TX_DONE:
            btdm_free(Parms->p.adv.txPdu->parms);
            btdm_free(Parms->p.adv.txPdu);
            break;
        
        default:
            break;
    }
}

static void pbap_callback(PbapClientCallbackParms *Parms)
{
    printf("pbap callback event = %d\r\n",Parms->event);
    switch (Parms->event) {
        case PBAP_EVENT_PARAMS_RX:
        {
            if ((Parms->oper == PBAPOP_PULL_PHONEBOOK) ||
                (Parms->oper == PBAPOP_PULL_VCARD_LISTING)) {
                    printf("miss calls: %d,pb size=%d\r\n",Parms->u.paramsRx.newMissedCalls,Parms->u.paramsRx.phonebookSize);
            }
        }
            break;
        case PBAP_EVENT_DATA_IND:
            bt_update_conn_status(BT_PROFILE_PBAP_DATA_IND, NULL, Parms);
            break;
        case PBAP_EVENT_TP_CONNECTED:
            bt_update_conn_status(BT_PROFILE_PBAP_CONN, NULL, Parms);
            break;
        case PBAP_EVENT_TP_DISCONNECTED:
            bt_update_conn_status(BT_PROFILE_PBAP_DISCONN, NULL, Parms);
            break;
        case PBAP_EVENT_CONTINUE:
            /* Always call continue to keep the commands flowing */
            PBAP_ClientContinue(Parms->client);
            break;
        case PBAP_EVENT_ABORTED:
            /* The requested operation was aborted. */
            break;

        case PBAP_EVENT_COMPLETE:
            bt_update_conn_status(BT_PROFILE_PBAP_COMP, NULL, Parms);
            break;
        default:
            break;
    }
}

#if BTDM_STACK_ENABLE_PAN
static void HandleFilters(PanUser *pan, uint16_t *range, uint16_t numRanges)
{
    BtStatus            status;
    uint8_t             i;
    PanControlRespPkt  *controlRespPkt;

    LOG_INFO(NULL, "Pan: Rx'd BNEP_CONTROL:NET_TYPE_SET %d Filter ranges\r\n", numRanges);
    for (i = 0; i < numRanges*2; i+=2) {
        LOG_INFO(NULL, "   ==>Range start: %#.4x\r\n", BEtoHost16((uint8_t *)&range[i]));
        LOG_INFO(NULL, "   ==>Range end: %#.4x\r\n", BEtoHost16((uint8_t *)&range[i+1]));
    }

    controlRespPkt = pvPortMalloc(sizeof(PanControlRespPkt));
    if (controlRespPkt == 0) {
        LOG_INFO(NULL, "PanApp: No Resources\r\n");
        return;
    }

    /* Set the response message */
    StoreBE16(controlRespPkt->message, BNEP_FR_OPERATION_SUCCESS);

    /* Send the Control Response packet */
    status = PAN_ControlResponse(pan, BNEP_FILTER_NET_TYPE_RESPONSE_MSG, controlRespPkt);
    LOG_INFO(NULL, "PanApp: PAN_ControlResponse() Returned 0x%04x\r\n", status);
    if (status != BT_STATUS_PENDING) {
        vPortFree(controlRespPkt);
    }
}

static void HandleAddrs(PanUser *pan, BNEP_ADDR *addr, uint16_t numRanges)
{
    BtStatus            status;
    uint8_t             i;
    PanControlRespPkt  *controlRespPkt;
    uint8_t             buffer[40];

    LOG_INFO(NULL, "Pan: Rx'd BNEP_CONTROL:MULTI_ADDR_SET %d Address ranges\r\n", numRanges);
    for (i = 0; i < numRanges*2; i+=2) {
        bnep_addr_ntoa((BNEP_ADDR *)(&addr[i]), (char *)buffer);
        LOG_INFO(NULL, "   ==>Address start: %s\r\n", buffer);
        bnep_addr_ntoa((BNEP_ADDR *)(&addr[i+1]), (char *)buffer);
        LOG_INFO(NULL, "   ==>Address end: %s\r\n", buffer);
    }

    controlRespPkt = pvPortMalloc(sizeof(PanControlRespPkt));
    if (controlRespPkt == 0) {
        LOG_INFO(NULL, "PanApp: No Resources\r\n");
        return;
    }

    /* Set the response message */
    StoreBE16(controlRespPkt->message, BNEP_FR_OPERATION_SUCCESS);

    /* Send the Control Response packet */
    status = PAN_ControlResponse(pan, BNEP_FILTER_MULTI_ADDR_RESPONSE_MSG, controlRespPkt);
    LOG_INFO(NULL, "PanApp: PAN_ControlResponse() Returned 0x%04x\r\n", status);
    if (status != BT_STATUS_PENDING) {
        vPortFree(controlRespPkt);
    }
}

/* ---------------------------------------------------------------------------
 *                      PAN PANU Packet handler
 * ---------------------------------------------------------------------------
 */
static void HandlePanuPacket(PanCallbackParms *Info)
{
    bool DisplayUnknownPackets = true;
    uint16_t    bodyLen, ipLen, ipTotLen;
    BtStatus    status;
    ArpHeader  *arpHdrIn;
    IpHeader   *ipHdr;
    UdpHeader  *udpHdr;
    DhcpHeader *dhcpHdr;
    IcmpHeader *icmpHdr;
    PanPacket  *panPkt = 0;
    BNEP_ADDR  *macAddr = 0;
    uint8_t     buff[1024];
    uint32_t    myIpAddr, dstIP;
    PanPacketType panPktType;

    switch (Info->type) {
    case PANPKT_ARP_REQUEST:
        LOG_INFO(NULL, "Received ARP Request\r\n");
        arpHdrIn = (ArpHeader *)(Info->pkt + ETHER_HDR_SIZE);
        /* The reply is already set up. Just forward it. */
        PAN_ForwardPkt(Info->pan, Info->pkt, Info->rxPktLen);
        break;

    case PANPKT_ARP_REPLY:
        LOG_INFO(NULL, "Received ARP Reply\r\n");
        arpHdrIn = (ArpHeader *)Info->pkt;
        myIpAddr = PAN_GetLocalIpAddr_v2();
        if (myIpAddr == BEtoHost32((uint8_t *)&arpHdrIn->dstIP) &&
            PAN_GetRemoteIpAddr_v2(Info->pan) == BEtoHost32((uint8_t *)&arpHdrIn->srcIP)) {
            /* This is a response to my arp request */
            PAN_SetRemoteMacAddr(Info->pan, arpHdrIn->srcMac);
            LOG_INFO(NULL, "Destination MAC address = %02x:%02x:%02x:%02x:%02x:%02x\r\n", 
                arpHdrIn->srcMac[0], arpHdrIn->srcMac[1], arpHdrIn->srcMac[2],
                arpHdrIn->srcMac[3], arpHdrIn->srcMac[4], arpHdrIn->srcMac[5]);
        }
        break;

    case PANPKT_IP:
        if (DisplayUnknownPackets == true) {
            LOG_INFO(NULL, "Received IPv4 Packet\r\n");
//            SOCKADDR_IN from;
//            ipHdr = (IpHeader *)Info->pkt;
//            memset((uint8_t*)&from, 0, sizeof(from));
//            from.sin_addr.S_un.S_addr = ipHdr->srcIP;
//            from.sin_family = AF_INET;
//            from.sin_port = 0;
//            LOG_INFO(NULL, "Remote IP address: %s, protocol: %d, Length: %d\r\n",
//                inet_ntoa(from.sin_addr), ipHdr->proto, ipHdr->totalLen);
        }
        break;

    case PANPKT_TCP:
        if (DisplayUnknownPackets == true) {
            LOG_INFO(NULL, "Received TCP Packet\r\n");
            ipHdr = (IpHeader *)Info->pkt;
        }
        break;

    case PANPKT_UDP:
        if (DisplayUnknownPackets == true) {
            LOG_INFO(NULL, "Received UDP Packet\r\n");
            ipHdr = (IpHeader *)Info->pkt;
            udpHdr = (UdpHeader *)((uint8_t *)ipHdr + sizeof(IpHeader));
        }
        break;

    case PANPKT_DHCP_REQUEST:
        LOG_INFO(NULL, "Received DHCP Request Packet\r\n");
        LOG_INFO(NULL, "    Not valid for a PANU!\r\n");
        break;

    case PANPKT_DHCP_REPLY:
        /* This is for me */
        {
            LOG_INFO(NULL, "Received my DHCP Reply Packet\r\n");
//            SOCKADDR_IN sAddr = {AF_INET, 0, 0, 0};
//            dhcpHdr = (DhcpHeader *)(Info->pkt + sizeof(IpHeader) + sizeof(UdpHeader));
//            if (dhcpHdr->options[2] == DHCPOFFER) {
//                sAddr.sin_addr.S_un.S_addr = dhcpHdr->yiaddr;
//                LOG_INFO(NULL, "DHCP server offered IP address %s\r\n", inet_ntoa(sAddr.sin_addr));

//                /* Set the device addresses used to make the DHCPREQUEST packet */
//                PAN_SetLocalIpAddr_v2(BEtoHost32((uint8_t *)&dhcpHdr->yiaddr));
//                PAN_SetDhcpAddr_v2(BEtoHost32((uint8_t *)&dhcpHdr->siaddr));
//                bodyLen = MakeDhcpPacket(INADDR_BROADCAST, &panPkt, BOOTP_CLIENT, DHCPREQUEST);
//                status = PAN_SendPkt(Info->pan, panPkt, PANPKT_DHCP_REQUEST, bodyLen);
//                LOG_INFO(NULL, "PanApp: PAN_SendPkt() Returned 0x%04x\r\n", status);
//            }
//            else if (dhcpHdr->options[2] == DHCPACK) {
//                sAddr.sin_addr.S_un.S_addr = dhcpHdr->yiaddr;
//                LOG_INFO(NULL, "DHCP server ACK'd IP address %s\r\n", inet_ntoa(sAddr.sin_addr));
//            }
        }
        break;

    case PANPKT_ETHER_PKT:
        PAN_ForwardPkt(Info->pan, Info->pkt, Info->rxPktLen);
        break;

    default:
        LOG_INFO(NULL, "PanApp: Rx'd unrecognized packet type %d\r\n", Info->type);
        break;
    }
}
static uint8_t bt_pan_state; //0 :disconnected 1:connected 
extern uint8_t tcp_connect_state;
uint8_t pan_get_open_state(void)
{
    return bt_pan_state;
}
void pan_callback(PanCallbackParms *Params)
{
    BtStatus            status;
    PanPacket          *panPkt = 0;
    ArpHeader          *arpHdr;
    uint16_t            bodyLen;
    PanSetupConnPkt    *setupPkt;
    PanControlRespPkt  *setupRespPkt;
    uint16_t            numRanges;
    PanPacketType       panPktType;

    switch (Params->event) {
    case PANEVENT_OPEN:
        LOG_INFO(NULL, "PanApp: Received PANEVENT_OPEN event\r\n");
        bt_pan_state = 1;
#if 1
        BD_ADDR addr;
        BNEP_ADDR addr_local, addr_remote;

        ME_ReadLocalBdAddr(&addr);
        memcpy(&addr_local.addr[0], &addr.A[0], BNEP_ADDR_SIZE);
        addr_remote = Params->pan->bnepPanu.bnepAddr;

        /* notify LWIP data link layer is established */
        bnep_lwip_netif_up(Params->pan, &addr_local, &addr_remote);
#else
        /* ARP to verify my Link Local address */
        panPktType = PANPKT_ARP_REQUEST;
        bodyLen = sizeof(ArpHeader);
        panPkt = pvPortMalloc(sizeof(PanPacket) + bodyLen);
        arpHdr = (ArpHeader *)panPkt->body;

        /* Set the target address to my initial Link Local address */
        PAN_MakeArpHdr(arpHdr, PAN_GetLocalIpAddr_v2());

        status = PAN_SendPkt(Params->pan, panPkt, panPktType, bodyLen);
        if (status != BT_STATUS_PENDING) {
            LOG_INFO(NULL, "PanApp: Send failed\r\n");
            vPortFree(panPkt);
            return;
        }
        LOG_INFO(NULL, "PanApp: Sent ARP to validate Link Local Address\r\n");
#endif
        break;

    case PANEVENT_SETUP_CONNECTION:
        LOG_INFO(NULL, "PanApp: Received PANEVENT_SETUP_CONNECTION event\r\n");
        /* Complete the open with a Setup Connection Request */
        setupPkt = pvPortMalloc(sizeof(PanSetupConnPkt));
        if (setupPkt == 0) {
            LOG_INFO(NULL, "PanApp: No Resources");
            return;
        }

        status = PAN_SetupConnection(Params->pan, setupPkt);
        LOG_INFO(NULL, "PanApp: PAN_SetupConnection() Returned 0x%04x\r\n", status);
        if (status != BT_STATUS_PENDING) {
            vPortFree(setupPkt);
        }
        break;

    case PANEVENT_SETUP_CONN_REQ:
        LOG_INFO(NULL, "PanApp: Received PANEVENT_SETUP_CONN_REQ event\r\n");
        /* Complete the open with a Setup Connection Response */
        setupRespPkt = pvPortMalloc(sizeof(PanControlRespPkt));
        if (setupRespPkt == 0) {
            LOG_INFO(NULL, "PanApp: No Resources\r\n");
            return;
        }

        /* The setup connection message has already been validated. The
         * status parameter contains the validation results of type
         * BnepSetupResponseMsg. Just copy the status into my response
         * and send it back.
         */
        StoreBE16(setupRespPkt->message, Params->status);
        status = PAN_ControlResponse(Params->pan, 
            BNEP_SETUP_CONNECTION_RESPONSE_MSG, setupRespPkt);
        LOG_INFO(NULL, "PanApp: PAN_ControlResponse() Returned 0x%04x\r\n", status);
        if (status != BT_STATUS_PENDING) {
            btdm_free(setupRespPkt);
        }
        break;

    case PANEVENT_CONTROL_IND:
        switch (Params->pkt[1]) {
        case BNEP_FILTER_NET_TYPE_SET_MSG:
            numRanges = Params->rxPktLen - BNEP_CONTROL_HDR_SIZE;
            numRanges /= ETHER_RANGE_SIZE;
            HandleFilters(Params->pan, (uint16_t *)(Params->pkt + 2), numRanges);
            break;

        case BNEP_FILTER_NET_TYPE_RESPONSE_MSG:
            LOG_INFO(NULL, "PanApp: Received FILTER_NET_TYPE_RESPONSE event\r\n");
            break;

        case BNEP_FILTER_MULTI_ADDR_SET_MSG:
            numRanges = Params->rxPktLen - BNEP_CONTROL_HDR_SIZE;
            numRanges /= BNEP_ADDR_SIZE * 2;
            HandleAddrs(Params->pan, (BNEP_ADDR *)(Params->pkt + 2), numRanges);
            break;

        case BNEP_FILTER_MULTI_ADDR_RESPONSE_MSG:
            LOG_INFO(NULL, "PanApp: Received MULTI_ADDR_RESPONSE event\r\n");
            break;

        default:
            break;
        }
        break;

    case PANEVENT_CLOSED:
        LOG_INFO(NULL, "PanApp: Received PANEVENT_CLOSED event\r\n");
        bt_pan_state = 0;
        tcp_connect_state = 0;
        bnep_lwip_netif_down();
        break;

    case PANEVENT_DATA_IND:
        LOG_INFO(NULL, "PanApp: Received PANEVENT_DATA_IND event\r\n");
        HandlePanuPacket(Params);
        break;

    case PANEVENT_DATA_HANDLED:
        LOG_INFO(NULL, "PanApp: Received PANEVENT_DATA_HANDLED event\r\n");
        vPortFree(Params->pkt);
        break;

    case PANEVENT_SDP_FAILED:
        LOG_INFO(NULL, "PanApp: Received PANEVENT_SDP_FAILED event\r\n");
        break;

    default:
        LOG_INFO(NULL, "PanApp: Received Unknown Event %d with status 0x%04x\r\n", Params->event,
                                                            Params->status);
        break;
    }
}
#endif

//void a2dp_device_callback(void *Device, const void *Parms)
//{    
//    switch(*(volatile uint8_t *)Parms){
//        case 1/*AVDEV_EVENT_AVDTP_CONNECT_IND*/:
//            AVDEV_SignalConnectRsp(Device, TRUE, a2dp_device_callback);
//            break;
//        default:
//            break;
//    }
//}

void app_btdm_struct_size_dump(void)
{
    printf("BTDM STRUCT SIZE: defined in stack.\r\n");
    extern void btdm_struct_size_dump(void);
    btdm_struct_size_dump();
    
    printf("BTDM STRUCT SIZE: defined in api.\r\n");
    printf("BtEvent is %d\r\n", sizeof(BtEvent));
    printf("BtRemoteDevice is %d\r\n", sizeof(BtRemoteDevice));

    printf("RfDeferEvent is %d\r\n", sizeof(RfDeferEvent));
    printf("RfChannel is %d\r\n", sizeof(RfChannel));

    printf("HfChannel is %d\r\n", sizeof(HfChannel));
    printf("HfCommand is %d\r\n", sizeof(HfCommand));
    printf("HfCallbackParms is %d\r\n", sizeof(HfCallbackParms));
    printf("HfgChannel is %d\r\n", sizeof(HfgChannel));
    printf("HfgCallbackParms is %d\r\n", sizeof(HfgCallbackParms));

    printf("A2dpStream is %d\r\n", sizeof(A2dpStream));
    printf("A2dpCallbackParms is %d\r\n", sizeof(A2dpCallbackParms));

    printf("AvdtpStream is %d\r\n", sizeof(AvdtpStream));
    printf("AvdtpConn is %d\r\n", sizeof(AvdtpConn));
    printf("AvdtpCallbackParms is %d\r\n", sizeof(AvdtpCallbackParms));

    printf("AvctpCallbackParms is %d\r\n", sizeof(AvctpCallbackParms));
    printf("AvctpChannel is %d\r\n", sizeof(AvctpChannel));
    printf("AvctpCmdFrame is %d\r\n", sizeof(AvctpCmdFrame));
    printf("AvctpRspFrame is %d\r\n", sizeof(AvctpRspFrame));

    printf("AvrcpChannel is %d\r\n", sizeof(AvrcpChannel));
    printf("AvrcpCallbackParms is %d\r\n", sizeof(AvrcpCallbackParms));
    printf("AvrcpAdvancedPdu is %d\r\n", sizeof(AvrcpAdvancedPdu));
    printf("AvrcpPlayerSetting is %d\r\n", sizeof(AvrcpPlayerSetting));
    printf("AvrcpPlayerStrings is %d\r\n", sizeof(AvrcpPlayerStrings));
    printf("AvrcpEqString is %d\r\n", sizeof(AvrcpEqString));
    printf("AvrcpRepeatString is %d\r\n", sizeof(AvrcpRepeatString));
    printf("AvrcpShuffleString is %d\r\n", sizeof(AvrcpShuffleString));
    printf("AvrcpScanString is %d\r\n", sizeof(AvrcpScanString));
    printf("AvrcpExtString is %d\r\n", sizeof(AvrcpExtString));
    printf("AvrcpCharSets is %d\r\n", sizeof(AvrcpCharSets));
    printf("AvrcpMediaInfo is %d\r\n", sizeof(AvrcpMediaInfo));
    printf("AvrcpMediaPlayStatus is %d\r\n", sizeof(AvrcpMediaPlayStatus));
    printf("AvrcpTrackStruct is %d\r\n", sizeof(AvrcpTrackStruct));
}

void app_bt_send_sco_data(void *channel, uint8_t seq, uint8_t *data, uint16_t length)
{
    BtPacket *sco_packet;
    HfChannel *Chan = channel;
    uint16_t data_length;
    uint8_t sub_seq = 0;
    uint8_t index;
    BtStatus status = BT_STATUS_SUCCESS;
    
    sco_packet = btdm_malloc(sizeof(BtPacket));
    if (sco_packet) {
        memset((void *)sco_packet, 0, sizeof(BtPacket));

        if (length == 57) {
            data_length = 60;
            sco_packet->data = btdm_malloc(data_length);
            sco_packet->data[0] = 0x01;
            if (seq & 0x01) {
                sub_seq = 0x30;
            }
            if (seq & 0x02) {
                sub_seq |= 0xc0;
            }
            sco_packet->data[1] = 0x08 | sub_seq;
            memcpy(&sco_packet->data[2], data, length);
            sco_packet->data[59] = 0x00;
            sco_packet->dataLen = data_length;
        }
        else {
            sco_packet->data = btdm_malloc(length);
            memcpy(sco_packet->data, data, length);
            sco_packet->dataLen = length;
        }
        for(index = 0; index < NUM_BT_DEVICES; index++)
        {
            if((uint32_t)&hfg_channel[index] == (uint32_t)channel){
                status = HFG_SendAudioData((HfgChannel *)Chan, sco_packet);
                break;
            }
            else if((uint32_t)&hf_channel[index] == (uint32_t)channel){
                status = HF_SendAudioData((HfChannel *)Chan, sco_packet);
                break;
            }
        }
        if((index == NUM_BT_DEVICES) || (status != BT_STATUS_PENDING)){
            btdm_free(sco_packet->data);
            btdm_free(sco_packet);
        }
    }
}

void app_bt_init(void)
{
//    app_btdm_struct_size_dump();
    #define EIR_DATA_SIZE 50
    uint8_t eir_data[EIR_DATA_SIZE];
    uint8_t index = 0;
    uint8_t rand_num[4];
    uint8_t hex_2_char[] = {'0', '1', '2','3', '4', '5','6', '7', '8','9', 'a', 'b','c', 'd', 'e', 'f'};
    
#if BTDM_STACK_ENABLE_HF
    hf_channel = btdm_malloc(sizeof(HfChannel) * NUM_BT_DEVICES);
#endif
#if BTDM_STACK_ENABLE_AG
    hfg_channel = btdm_malloc(sizeof(HfgChannel) * NUM_BT_DEVICES);
#endif
    Stream = btdm_malloc(sizeof(A2dpStream) * NUM_STREAMS);
#if BTDM_STACK_ENABLE_AVRCP
    rcpCtChannel = btdm_malloc(sizeof(AvrcpChannel) * NUM_BT_DEVICES);
    rcpTgChannel = btdm_malloc(sizeof(AvrcpChannel) * NUM_BT_DEVICES);
#endif
//    scoPacket = btdm_malloc(sizeof(BtPacket));
//    memset((void *)scoPacket, 0, sizeof(BtPacket));
//    scoPacket->data = btdm_malloc(120);
    
    ME_InitHandler(&securityHandler);
    securityHandler.callback = security_event_handler;
    SEC_RegisterPairingHandler(&securityHandler);
    SEC_RegisterAuthorizeHandler(&securityHandler);

    /* Set our promiscuous link policies */
    ME_SetDefaultLinkPolicy(((BLP_MASTER_SLAVE_SWITCH|BLP_SNIFF_MODE)&(~BLP_MASK)), 
                                        ((BLP_MASTER_SLAVE_SWITCH|BLP_SNIFF_MODE)&(~BLP_MASK)));
#if BT_ADDR_RANDOM_ENABLE                                        
    if(flashdb_get(FDB_KEY_USER_RANDOM_SEED, (void *)&rand_num[0], 4) != 0){
        default_name[7] = hex_2_char[(rand_num[1]&0xf0) >> 4];
        default_name[8] = hex_2_char[(rand_num[1]&0x0f)];
        default_name[9] = hex_2_char[(rand_num[0]&0xf0) >> 4];
        default_name[10] = hex_2_char[(rand_num[0]&0x0f)];
    }
#endif

    ME_SetLocalDeviceName((const uint8_t *)default_name, sizeof(default_name));
    memset(&eir_data[0],0,EIR_DATA_SIZE);
    eir_data[index++] = sizeof(default_name) + 1;
    eir_data[index++] = 0x09;
    memcpy(&(eir_data[index]),default_name,sizeof(default_name));
    ME_SetExtInquiryRsp(0x01,eir_data,sizeof(default_name)+1);

    SEC_SetBondingMode(GENERAL_BONDING);
    SEC_SetIoCapabilities(IO_NO_IO);
    ME_InitHandler(&(globalHandler));
    globalHandler.callback = me_callback;
    ME_RegisterGlobalHandler(&globalHandler);
//    ME_SetEventMask(&globalHandler, (BEM_ACCESSIBLE_CHANGE  |
//                                        BEM_LINK_CONNECT_CNF   |
//                                        BEM_LINK_CONNECT_IND   |
//                                        BEM_LINK_DISCONNECT    |
//                                        BEM_ROLE_CHANGE     |
//                                        BEM_MODE_CHANGE     |
//                                        BEM_INQUIRY_COMPLETE    |
//                                        BEM_INQUIRY_RESULT  |
//                                        BEM_INQUIRY_CANCELED));
    ME_SetEventMask(&globalHandler, BEM_ALL_EVENTS);

    ME_RegisterAcceptHandler(&globalHandler);  

    secParms.level = (BSL_SECURITY_L2_IN | BSL_SECURITY_L2_OUT);//HF_SECURITY_SETTINGS;
    secParms.pinLen = 4;
    
#if BTDM_STACK_ENABLE_HF
    HF_SetSupportedFeature(HF_SDK_FEATURES);
    for (uint8_t i=0; i<NUM_BT_DEVICES; i++) {
        HF_RegisterSec(&hf_channel[i], hf_callback, &secParms);
    }
#endif
    
    ME_SetClassOfDevice( COD_AUDIO 
                       | COD_MAJOR_AUDIO 
                       | COD_MINOR_AUDIO_HANDSFREE);
                       
#if BTDM_STACK_ENABLE_AG
    HFG_SetSupportedFeature(HFG_SDK_FEATURES);
    for (uint8_t i=0; i<NUM_BT_DEVICES; i++) {
        HFG_RegisterSec(&hfg_channel[i], hfg_callback, &secParms);
    }
#endif

#if BTDM_STACK_ENABLE_A2DP_SNK
    /* Register SBC stream sink */ 
    sbcSnkCodec1.codecType = AVDTP_CODEC_TYPE_SBC;
    sbcSnkCodec1.elemLen = sizeof(SbcSnkElements);
    sbcSnkCodec1.elements = SbcSnkElements;

#if BTDM_STACK_ENABLE_AAC
    aacSnkCodec1.codecType = AVDTP_CODEC_TYPE_MPEG2_4_AAC;
    aacSnkCodec1.elemLen = sizeof(AacSnkElements);
    aacSnkCodec1.elements = AacSnkElements;
#else
    sbcSnkCodec2.codecType = AVDTP_CODEC_TYPE_SBC;
    sbcSnkCodec2.elemLen = sizeof(SbcSnkElements);
    sbcSnkCodec2.elements = SbcSnkElements;
#endif
    
    Stream[0].type = A2DP_STREAM_TYPE_SINK;
    Stream[1].type = A2DP_STREAM_TYPE_SINK;
    A2DP_Register(&Stream[0], &sbcSnkCodec1, a2dp_callback);
#if BTDM_STACK_ENABLE_AAC
    A2DP_Register(&Stream[1], &aacSnkCodec1, a2dp_callback);
#else
    A2DP_Register(&Stream[1], &sbcSnkCodec2, a2dp_callback);
#endif
#endif
    
#if BTDM_STACK_ENABLE_PAN
    PanService pan_service;
    pan_service.type = 0x1115;  // PANU
    pan_service.callback = pan_callback;
    PAN_Register(&pan_service);
    bnep_lwip_init();
#endif

#if BTDM_STACK_ENABLE_A2DP_SRC
    /* Register SBC stream src */ 
    sbcSrcCodec1.codecType = AVDTP_CODEC_TYPE_SBC;
    sbcSrcCodec1.elemLen = sizeof(SbcSrcElements);
    sbcSrcCodec1.elements = SbcSrcElements;
    Stream[2].type = A2DP_STREAM_TYPE_SOURCE;
    Stream[3].type = A2DP_STREAM_TYPE_SOURCE;
    A2DP_Register(&Stream[2], &sbcSrcCodec1, a2dp_callback);
    A2DP_Register(&Stream[3], &sbcSrcCodec1, a2dp_callback);
#endif
    
//    AVDEV_ListenSignalLink(a2dp_device_callback);
    
#if BTDM_STACK_ENABLE_AVRCP || BTDM_STACK_ENABLE_A2DP_SNK || BTDM_STACK_ENABLE_A2DP_SRC
    for (uint8_t i=0; i<NUM_BT_DEVICES; i++) {
        AVRCP_RegisterSec(&rcpCtChannel[i], avrcp_callback, AVRCP_CT|AVRCP_TG, &secParms);
#if AVRCP_ADVANCED_TARGET == XA_ENABLED
        AVRCP_TgSetEventMask(&rcpCtChannel[i], AVRCP_ENABLE_VOLUME_CHANGED);
#endif
        //AVRCP_RegisterSec(&rcpTgChannel[i], avrcp_callback, AVRCP_TG, &secParms);
    }
#endif

    ME_SetAccessibleModeC(BAM_NOT_ACCESSIBLE, &access_mode_nc);
    ME_SetAccessibleModeNC(BAM_GENERAL_ACCESSIBLE, &access_mode_nc);
}

